package com.artoxian.application;
import java.io.BufferedReader;
import java.io.File;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.UnsupportedEncodingException;
import java.util.ArrayList;
import java.util.List;

import org.apache.http.HttpHost;
import org.apache.http.HttpResponse;
import org.apache.http.NameValuePair;
import org.apache.http.client.ClientProtocolException;
import org.apache.http.client.HttpClient;
import org.apache.http.client.config.RequestConfig;
import org.apache.http.client.entity.EntityBuilder;
import org.apache.http.client.entity.UrlEncodedFormEntity;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.impl.client.HttpClientBuilder;
import org.apache.http.message.BasicNameValuePair;
import org.apache.log4j.Logger;

import com.artoxian.exception.GarminConnectorException;

public class GarminConnector {
	private static Logger log = Logger.getLogger(GarminConnector.class);
	
	// URL strings
	private final String LOGIN_URL = "https://connect.garmin.com/signin/";
	private final String UPLOAD_URL = "http://connect.garmin.com/proxy/upload-service-1.1/json/upload/";
	private final String LIST_URL = "http://connect.garmin.com/proxy/activity-search-service-1.2/json/activities";
	
	// Connection variables
	private final HttpClient httpClient;
	private RequestConfig config;
	
	// Log in variables
	private boolean loggedIn;
	
	/*
	 * CONSTRUCTORS
	 */
	public GarminConnector() {
		// Set logged in to false
		loggedIn = false;
		
		// Create the client
		httpClient = HttpClientBuilder.create().build();
        
		// Set up the proxy (debug only)
		HttpHost proxy = new HttpHost("127.0.0.1", 8888, "http");
        config = RequestConfig.custom().setProxy(proxy).build();
        
        // Initialise the user session
        try { 
        	initialiseCookies();
        } catch (Exception exception) {
        	log.error(exception);
        }
	}	
	
	/*
	 * GETTER/SETTERS
	 */
	public boolean isLoggedIn() {
		return loggedIn;
	}
	
	/*
	 * HTTP METHODS
	 */
	/**
	 * Performs the initial GET request on the login page to obtain the relevant cookies
	 */
	private void initialiseCookies() throws IOException {
        httpClient.execute(new HttpGet(LOGIN_URL));
	}
	
	public void login(String username, String password) throws UnsupportedEncodingException, GarminConnectorException {		
		// Create the POST request
		HttpPost request = new HttpPost(LOGIN_URL);
		
		// Add the login parameters
		List <NameValuePair> nvps = new ArrayList<NameValuePair>();
		nvps.add(new BasicNameValuePair("login", "login"));
		nvps.add(new BasicNameValuePair("login:loginUsernameField", username));
		nvps.add(new BasicNameValuePair("login:password", password));
		nvps.add(new BasicNameValuePair("login:signInButton", "Sign In"));
		nvps.add(new BasicNameValuePair("javax.faces.ViewState", "j_id1"));
		request.setEntity(new UrlEncodedFormEntity(nvps));
		
		// Perform the post
		try {
			httpClient.execute(request);
		} catch (Exception exception) {
			throw new GarminConnectorException(exception.getMessage(), exception);
		}
		
		// Set the internal variables
		loggedIn = true;
		log.info("Logged in to Garmin Connect as: " + username);
	}
	
	public List<String> setExistingActivityIds() throws IOException, GarminConnectorException {
		// Throw error if user isn't logged in
		if (!loggedIn) {
			throw new GarminConnectorException("User Not Logged In");
		}
		
		// Submit the GET
        HttpResponse response = httpClient.execute(new HttpGet(LIST_URL));
        
        // Return type
        ArrayList<String> externalIds = new ArrayList<String>();
        
        // Build a string from the response
        BufferedReader reader = new BufferedReader(new InputStreamReader(response.getEntity().getContent(), "UTF-8"));
        //StringBuilder builder = new StringBuilder();
        String line = reader.readLine();
        while (line != null) {
        	// Remove all spaces
        	line = line.replaceAll(" ", "");
        	
        	// Only process the line if it is the externalId
        	if (line.startsWith("\"externalId\"")) {
        		line = line.substring(line.indexOf(':') + 2, line.lastIndexOf('\"'));
        		externalIds.add(line.substring(line.indexOf(':') + 1, line.length()));
        	}
        	
        	// Read the next line
        	line = reader.readLine();
        }
        
        // return all external ids
        return externalIds;
	}
	
	/**
	 * Submits a file for the logged in user
	 */
	public void upload(File file) throws IOException, GarminConnectorException {
		// Throw error if file does not exist
		if (file == null || !file.exists()) {
			throw new IOException("File cannot be found: " + file.getAbsolutePath());
		}
		
		// Throw error if user isn't logged in
		if (!loggedIn) {
			throw new GarminConnectorException("User Not Logged In");
		}
		
        // Initialise the post
		HttpPost httpPost = new HttpPost(UPLOAD_URL);
		//httpPost.setConfig(config);
		
		// Add the file
		EntityBuilder testBuilder = EntityBuilder.create();
		testBuilder.setFile(file);
		httpPost.setEntity(testBuilder.build());
		
		// Perform the post
		httpClient.execute(httpPost);
		log.info("File successfully uploaded to Garmin Connect: " + file.getAbsolutePath());
    }
}
